Date		:	24 septembre 1992
		Logiciel	:	Data Recovery Software V1.22
		Protection	:       
		Outils		:	SOFT-ICE V2.50
		Temps pass�	:	3 Heures
		Programme	:       DRS.EXE
		Soci�t�		:	SHAREWARE
		Divers		:	Programme �crit en BASIC Microsoft
					Compil�.
		Origine		:	Dr C. JP
		Num�ro		:	185


	Le probl�me soumit par Mr X �tait de savoir pourquoi ce logiciel
	de r�cup�ration de secteurs perdus ou endommag�s insistait lourdement
	lors de l'affichage pour ne pas afficher compl�tement les octets ( ou
	plus exactement les mots ) qui comportaient plus d'un 0 hexad�cimal.
	Exemple, si le mot � afficher �tait 3BC1 ce mot �tait bien affich�.
	Par contre si le mot �tait nul comme 0000, on affichait que 0. Et dans
	le cas de 00AD on affichait AD. Pour le mode TEXTE cel� n'avait pas 
	d'importance puisqu'aucun caract�re ASCII n'est repr�sent� par 0.
	Par contre en notation HEXA c'est beaucoup plus g�nant. N'est-ce pas ?
	Bien que la notation pr�sent�e dans le programme ne favorise en rien
	le rep�rage du num�ro d'octet, du d�but ou de la fin de la FAT et/ou
	des r�pertoires.

	Le plus dur c'est de comprendre ce que le programmeur a voulu faire.
	Mais dans le cas pr�sent il s'agit de savoir ce qu'� compil� le
	compilateur lors de la transcription du BASIC en langage machine.
	Pourquoi du BASIC ? 
	
	   Parceque les chaines ASCII ne se terminent pas par le signe $.
	   Ce qui �limine l'ASM, il reste donc du PASCAL, BASIC ou du C.
	   En examinant la fin du fichier ( la ou le LINKER laisse des traces )
	   on trouve les in�vitables instructions BASIC.

	En d�roulant le programme au pas � pas on s'aper�oit que l'affichage
	aussi bien en HEXA qu'en ASCII se fait par deux CALL appel�s tour �
	tour situ�s en CS:83D8 et CS:8406.

	CS=0DBE
	CS:83D8	CALL 19CA:0002
	CS:8406 CALL 19CA:0002

	En fait chaque CALL affiche un octet ( dans le cas o� l'affichage HEXA
	est s�lectionn�e ).
	Dans ces CALL l'instruction situ�e en 19CA:0087 charge les caract�res
	ainsi que leur atribut en B800:offset qui repr�sente comme chacun le
	sait l'adresse de la m�moire �cran en mode texte.
	Ces caract�res "arrivent et partent" par des LDSB et MVSB. Il suffit
	de se positionner sur DS:SI pour savoir d'o� ils viennent. En
	surveillant la zone d'arriv�e tout en faisant d�filer le programme on
	d�couvre le "transporteur": il s'agit du CALL 1AD7:5901 appel� en
	CS:837B. A l'int�rieur de ce CALL on trouve la routine qui transforme
	les caract�res HEXA en ASCII pour l'affichage puis les places en DS:SI
	pour �tre exploit�s par les deux CALL 19CA:0002.
	Dans le CALL 1AD7:5901 les octets arrivent par deux dans le registre BX
	avec l'instruction MOV BX,[BP+06] situ� en 1AD7:5913.
	Et en CS:591E se trouve la routine de transcription HEXA --> ASCII pour
	l'affichage �cran:

	CS=1AD7
	CS:591E	FD	STD
    ��>	CS:591F	8AC3	MOV	AL,BL
    �	CS:5921	22C5	AND	AL,CH
    �	CS:5923	0490	ADD	AL,90
    �	CS:5925	27	DAA
    �	CS:5926	1440	ADC	AL,40
    �	CS:5928	27	DAA	
    �	CS:5929	AA	STOSB
    �	CS:592A	FEC4	INC 	AH
    �	CS:592C	51	PUSH	CX
    �	CS:592D	32ED	XOR	CH,CH
    �	CS:592F	D1EA	SHR	DX,1   <ͻ	; On SHIFT et ROTATE 4 fois
    �	CS:5931	D1DB	RCR	BX,1     �	; BX et DX.
    �	CS:5933	E2FA	LOOP	592F   >ͼ
    �	CS:5935	59	POP	CX
    �	CS:5936	53	PUSH	BX
    �	CS:5937	0BDA	OR	BX,DX
    �	CS:5939	5B	POP	BX
    ��<	CS:593A	75E3	JNZ	591F
	  :		.
	  :		.
	CS:.... 	Suite

	En analysant la fa�on dont on sort de la boucle lorsque le mot qui
	se pr�sente comporte 4 zeros on s'aper�oit que le premier zero est
	trait� normalement puis l'on sort de la boucle. En fait BX comportant
	le contenu du mot � afficher, ce dernier est shift� � droite 4 fois
	apr�s chaque digit trait� ( LOOP situ� en CS:5933 ). Donc le premier
	0 devient 30  ( ASCII ) puis BX contenant 0000 shift� cel� fait
	toujours 0000. Et le test OR BX,DX donnant zero on sort par CS:593A.
	R�sultat pratique l'affichage de 0000 se traduit par un 0 sur l'�cran.
	Dans le cas ou BX contient 3BC1 ( 4 digits HEXA ) on a beau shifter
	4 fois � chaque digit le contenu de BX ne sera pas nul avant que l'on
	ai trait� tous les chiffres.
 
	Donc 0000 donne   0
	     000C donne   C
	     00AD donne  AD
	     032A donne 32A

	Conclusion.
	-----------

	Il faut donc que dans tous les cas la boucle se fasse quatre fois
	sans interruption. Pour cel� il est n�cessaire d'agir sur le test en
	1AD7:593A. Yaka !
	La premi�re id�e qui vient c'est d'agir sur le contenu d'un des deux
	registres que l'instruction JNZ teste. Bonne id�e mais quasi ir�alisable
	car d'une part BX contient le mot de 16 bits � afficher et DX une valeur
	impossible � modifier sans obtenir un affichage erratique. L'initiali-
	sation de ce registre doit rester ce qu'elle �tait depuis 1AD7:5901
	( initialisation de DX ).
	Il faut donc utiliser une variable externe � cette boucle que l'on
	initialise � quatre puis que l'on d�cr�mente chaque fois qu'un digit
	est trait�. Plus facile � dire qu'� faire parceque cel� prendra au moins
	4 � 5 instructions que je ne vois pas o� loger. La seule fa�on de faire
	c'est de trouver une zone inexploit�e, y sauter tester la variable en
	question puis revenir � la boucle de test originelle. Si le programme
	avait �t� �crit en ASM il aurait �t� difficile de trouver une zone
	libre, alors que dans le cas pr�sent il suffit de r�cup�rer une des
	zones inexploit�es par le passage au linker. Il est facile de les
	trouver puisqu'elles sont en mode texte. Pour ne pas compliquer la
	programmation il vaut mieux essayer de trouver une de ces zones dans
	le segment courant.
	Une de ces zone existe en CS:8E85. 
	La premi�re chose � faire est d'y sauter en y reportant toutes les
	instructions �cras�es par le saut. Dans le cas pr�sent je d�cide de
	placer un saut juste avant le test JNZ en 1AD7:5937. Ce saut prend
	3 octets, je vais donc �craser les deux instructions OR BX,DX ainsi
	que le POP BX. Il faudra donc restaurer ces deux instructions avant
	de revenir au saut JNZ en CS:593A.
	
	Voici ci-dessous la boucle modifi�e:

	CS=1AD7
	CS:591E	FD	STD
    ��>	CS:591F	8AC3	MOV	AL,BL
    �	CS:5921	22C5	AND	AL,CH
    �	CS:5923	0490	ADD	AL,90
    �	CS:5925	27	DAA
    �	CS:5926	1440	ADC	AL,40
    �	CS:5928	27	DAA	
    �	CS:5929	AA	STOSB
    �	CS:592A	FEC4	INC 	AH
    �	CS:592C	51	PUSH	CX
    �	CS:592D	32ED	XOR	CH,CH
    �	CS:592F	D1EA	SHR	DX,1   <ͻ	; On SHIFT et ROTATE 4 fois
    �	CS:5931	D1DB	RCR	BX,1     �	; BX et DX.
    �	CS:5933	E2FA	LOOP	592F   >ͼ
    �	CS:5935	59	POP	CX
    �	CS:5936	E94B35	JMP	8E85		; Je saute dans la zone texte.
    ��<	CS:593A	75E3	JNZ	591F
	  :		.
	  :		.
	CS:.... 	Suite

	J'aurais ( r�flexion faite ) pu charger DS par CS mais ne voulant pas
	rajouter deux instructions suppl�mentaires PUSH DS et son pendant le
	POP, j'ai forc� la variable � tester dans le segment CS.
	Voici donc ce qui permet de tourner quatre fois dans la boucle 
	quelque soit le contenu � afficher:

   CS:8E85 33DB 	XOR BX,BX	      ;JE ME SERT DE BX POUR TESTER LE	
   CS:8E87 2E3A1E9D8E   CMP BYTE BX,CS:[8E9D] ;CONTENU DE MA VARIABLE [8E9D].
   CS:8E8C 7506	        JNZ 8E94	      ;SI # 0 ON SAUTE A DECREMENTE.
   CS:8E8E 2EC6069D8E04 MOV BYTE CS:[8E9D],04 ;SI = 0 ON REPLACE 4 DANS [8E9D].
   CS:8E94 2EFE0E9D8E   DEC BYTE CS:[8E9D]    ;ON DECREMENTE LE CONTENU DE 8E9D.
   CS:8E99 5B           POP BX                ;JE REMETS LE [BX] ECRASE.
   CS:8E9A E99DCA	JMP 593A	      ;JE REVIENS A MA BOUCLE D'ORIGINE.	 
   CS:8E9D 04           ; CECI EST MA VARIABLE SITUEE DANS LE SEGMENT CODE.
	
	Bien s�r en se cassant la t�te je suis persuad� que d'autres solutions
	existent, peut-�tre plus simples. 

	FREDDY